package com.wjyf.reducing_bento.service.impl;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wjyf.reducing_bento.model.bo.CommodityInfoBo;
import com.wjyf.reducing_bento.common.ErrorCode;
import com.wjyf.reducing_bento.model.dto.CommodityInfoDto;
import com.wjyf.reducing_bento.model.dto.OrderIndentDto;
import com.wjyf.reducing_bento.exception.BusinessException;
import com.wjyf.reducing_bento.mapper.IndentMapper;
import com.wjyf.reducing_bento.model.entity.*;
import com.wjyf.reducing_bento.service.*;
import com.wjyf.reducing_bento.model.vo.IndentInfoVo;
import io.swagger.models.auth.In;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

import static com.wjyf.reducing_bento.constant.CouponReceiveConstant.*;
import static com.wjyf.reducing_bento.constant.IndentConstant.*;
import static com.wjyf.reducing_bento.constant.InviteRecordConstant.*;


/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author wjyf
 * @since 2023-03-14 08:36:33
 */
@Service
@Log4j2
public class IndentServiceImpl extends ServiceImpl<IndentMapper, IndentBean> implements IndentService {


    @Resource
    private UserService userService;

    @Resource
    private CommodityService commodityService;

    @Resource
    private CouponService couponService;

    @Resource
    private InviteRewardService inviteRewardService;

    @Resource
    private InviteRecordService inviteRecordService;

    @Resource
    private CouponReceiveService couponReceiveService;

    @Resource
    private CouponLogsService couponLogsService;

    @Resource
    private IndentMapper indentMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean order(OrderIndentDto orderIndentDto, HttpServletRequest request) {
        // 校验参数
        if (orderIndentDto == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        List<CommodityInfoDto> codAll = orderIndentDto.getCodAll();
        Integer cpId = orderIndentDto.getCpId();
        String remark = orderIndentDto.getRemark();
        if (codAll == null || codAll.isEmpty() || StringUtils.isBlank(remark)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        UserBean user = userService.getUser(request);
        synchronized (this) {
            //订单金额
            double beforeMoney = 0;
            //do集合
            List<CommodityInfoBo> commodityInfoBoList = new ArrayList<>();
            //需要更新商品的集合
            List<CommodityBean> updateCommodityBeanList = new ArrayList<>();
            // 校验商品是否存在或库存足够
            for (CommodityInfoDto commodityInfoDto : codAll) {
                CommodityBean commodityBean = commodityService.getById(commodityInfoDto.getCodId());
                //商品不存在
                if (commodityBean == null) {
                    throw new BusinessException(ErrorCode.OPERATION_ERROR, "商品id为" + commodityInfoDto.getCodId() + "的商品不存在");
                }
                //库存不够
                if (commodityBean.getCodStock() < commodityInfoDto.getCodNum()) {
                    throw new BusinessException(ErrorCode.OPERATION_ERROR, "商品" + commodityBean.getCodName() + "库存不足");
                }
                // 商品金额相加
                beforeMoney += commodityBean.getCodPrice() * commodityInfoDto.getCodNum();
                //对应商品的库存减去
                commodityBean.setCodStock(commodityBean.getCodStock() - commodityInfoDto.getCodNum());
                updateCommodityBeanList.add(commodityBean);
                //dto 转 do
                CommodityInfoBo commodityInfoBo = new CommodityInfoBo();
                commodityInfoBo.setCodName(commodityBean.getCodName())
                        .setCodId(commodityInfoDto.getCodId())
                        .setCodNum(commodityInfoDto.getCodNum());
                commodityInfoBoList.add(commodityInfoBo);
            }
            // 使用优惠卷后的金额
            double afterMoney = beforeMoney;
            CouponBean couponBean = null;
            CouponReceiveBean receiveBean = null;
            boolean updateCouponReceive = true;
            boolean updateUserBean = true;
            // 有没有使用优惠卷
            if (cpId != null && cpId > 0) {
                // 查询优惠卷
                couponBean = couponService.getById(cpId);
                if (couponBean == null) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "该优惠卷不存在");
                }
                //查询优惠卷领取记录
                receiveBean = couponReceiveService.getOne(new LambdaQueryWrapper<CouponReceiveBean>()
                        .eq(CouponReceiveBean::getUId, user.getUId())
                        .eq(CouponReceiveBean::getStatus, NOT_USED)
                        .eq(CouponReceiveBean::getCouponId, cpId));
                if (receiveBean == null) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "您尚未领取该优惠卷");
                }
                // 计算金额
                //金额满
                double fullMoney = couponBean.getFullMoney().doubleValue();
                //优惠卷金额
                double money = couponBean.getMoney().doubleValue();
                // 可以使用优惠卷
                if (fullMoney <= beforeMoney) {
                    // 达到满减金额
                    afterMoney -= money;
                    receiveBean.setStatus(USED);
                }
            }
            //更新用户余额
            UserBean userBean = userService.getById(user.getUId());
            if (userBean.getUBalanc() < afterMoney) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "用户余额不足");
            }


            BigDecimal balance = BigDecimal.valueOf(userBean.getUBalanc());
            BigDecimal afterMoneyBigDecimal = BigDecimal.valueOf(afterMoney);
            userBean.setUBalanc(balance.subtract(afterMoneyBigDecimal).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
            updateUserBean = userService.updateById(userBean);
            LocalDateTime now = LocalDateTime.now();
            // 一天开始的时间
            LocalDateTime beginOfDay = LocalDateTimeUtil.beginOfDay(now);
            //构造订单实体类
            IndentBean indentBean = new IndentBean()
                    .setUId(user.getUId())
                    .setUPhon(user.getUPhon())
                    .setInFlag(PROGRESSING)
                    .setCodAll(JSONUtil.toJsonStr(commodityInfoBoList))
                    .setInStartTime(now)
                    .setCpId(Math.toIntExact(couponBean == null ? NOT_USE_COUPON : couponBean.getId()))
                    .setInSum(BigDecimal.valueOf(afterMoney))
                    .setIsLunch(isLunch(now, beginOfDay) ? LUNCH_FLAG : DINNER_FLAG)
                    .setRemark(remark);
            //往订单表插入数据
            boolean saveIndentBean = save(indentBean);
            //更新商品库存
            boolean updateCommodityBean = commodityService.updateBatchById(updateCommodityBeanList);
            boolean saveCouponLogsBean = true;
            if (receiveBean != null) {
                //修改优惠卷信息
                updateCouponReceive = couponReceiveService.updateById(receiveBean);
                // 优惠卷消费记录表
                CouponLogsBean couponLogsBean = new CouponLogsBean();
                couponLogsBean.setUId(user.getUId())
                        .setCouponReceiveId(receiveBean.getCouponId())
                        .setOrderNumber(String.valueOf(indentBean.getInId()))
                        .setOrderOriginalAmount(BigDecimal.valueOf(beforeMoney))
                        .setCouponAmount(couponBean == null ? BigDecimal.valueOf(0) : couponBean.getMoney())
                        .setOrderFinalAmount(BigDecimal.valueOf(afterMoney))
                        .setCreateTime(new Date())
                        .setStatus(1);
                saveCouponLogsBean = couponLogsService.save(couponLogsBean);
            }

            if (!saveIndentBean || !updateCommodityBean || !updateCouponReceive || !saveCouponLogsBean || !updateUserBean) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR);
            }
            // 下单基本完成，开始奖励
            InviteRecord inviteRecord = inviteRecordService.getOne(new LambdaQueryWrapper<InviteRecord>().eq(InviteRecord::getInviteeId, user.getUId()));
            if (inviteRecord != null && inviteRecord.getFlag().equals(FIRST_ORDER)) {
                // 下单用户是被别人邀请的注册的，并且是第一次下单
                inviteRecord.setFlag(FIRST_ORDER_PROGRESSING).setRemark(FIRST_ORDER_REMARKS);
                boolean update = inviteRecordService.updateById(inviteRecord);
                //// 查询邀请人信息
                //UserBean inviter = userService.getById(inviteRecord.getInviterId());
                //// 保存奖励记录并且给与奖励
                //inviteRewardService.saveInviteRewardInfoAndPulsBalance(inviter.getUId(), user.getUId());
            }

        }
        return true;
    }

    @Override
    public Page<IndentInfoVo> getPage(Long current, Long pageSize, Long uId, Integer flag) {
        if (current == null || pageSize == null || current <= 0 || pageSize == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        LambdaQueryWrapper<IndentBean> wrapper = new LambdaQueryWrapper<IndentBean>().orderByDesc(IndentBean::getInId);
        if (uId != null) {
            wrapper.eq(IndentBean::getUId, uId);
        }
        if (flag != null) {
            wrapper.eq(IndentBean::getIsLunch, flag);
        }
        Page<IndentBean> page = page(new Page<IndentBean>(current, pageSize), wrapper);
        List<IndentInfoVo> indentInfoVoList = page.getRecords().stream().map(indentBean -> {
            IndentInfoVo indentInfoVo = new IndentInfoVo();
            indentInfoVo.setInId(indentBean.getInId());
            indentInfoVo.setUId(indentBean.getUId());
            indentInfoVo.setCommodityInfoBoList(JSONUtil.toList(indentBean.getCodAll(), CommodityInfoBo.class));
            indentInfoVo.setInStartTime(indentBean.getInStartTime());
            indentInfoVo.setInFlag(indentBean.getInFlag());
            indentInfoVo.setInSum(indentBean.getInSum());
            indentInfoVo.setIsLunch(indentBean.getIsLunch());
            if (!indentBean.getCpId().equals(-1)) {
                CouponBean couponBean = couponService.getById(indentBean.getCpId());
                indentInfoVo.setCouponBean(couponBean);
            }
            UserBean user = userService.getById(indentBean.getUId());
            indentInfoVo.setUName(user.getUName());
            return indentInfoVo;
        }).collect(Collectors.toList());
        Page<IndentInfoVo> indentInfoVoPage = new Page<>(current, pageSize);
        indentInfoVoPage.setTotal(page.getTotal()).setRecords(indentInfoVoList);
        return indentInfoVoPage;
    }

    @Override
    public Map<String, Long> getAnalyseMap() {


        //获取当前时间的年月日
        LocalDateTime now = LocalDateTime.now();
        // 一天开始的时间
        LocalDateTime beginOfDay = LocalDateTimeUtil.beginOfDay(now);
        //一天结束的时间
        LocalDateTime endOfDay = LocalDateTimeUtil.endOfDay(now);

        long lunchCount = indentMapper.selectCount(new LambdaQueryWrapper<IndentBean>()
                .ge(IndentBean::getInStartTime, beginOfDay)
                .le(IndentBean::getInStartTime, endOfDay)
                .eq(IndentBean::getIsLunch, LUNCH_FLAG));
        long dinnerCount = indentMapper.selectCount(new LambdaQueryWrapper<IndentBean>()
                .ge(IndentBean::getInStartTime, beginOfDay)
                .le(IndentBean::getInStartTime, endOfDay)
                .eq(IndentBean::getIsLunch, DINNER_FLAG));
        HashMap<String, Long> resMap = new HashMap<>();
        resMap.put(DINNER, dinnerCount);
        resMap.put(LUNCH, lunchCount);
        return resMap;
    }

    /**
     * 取消订单：先判断是否能取消订单(包括判断订单是否是他本人的) -> 订单状态设置为已取消 -> 用户金额恢复 -> 商品库存+1 -> 优惠卷消费记录删除 -> 优惠卷设置为未使用或者已经过期 ->
     * 午餐12点以后不能取消晚餐八点以后
     *
     * @param inId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean cancelIndent(Long inId) {
        if (inId == null || inId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //String inIdString = String.valueOf(inId);
        synchronized (this) {
            LambdaQueryWrapper<IndentBean> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(IndentBean::getInId, inId).eq(IndentBean::getInFlag, PROGRESSING);
            IndentBean indentBean = this.getOne(wrapper);
            if (indentBean == null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "该订单不存在或者已经取消");
            }
            // 用户ID
            Long uId = indentBean.getUId();
            // 用户手机号码
            String uPhon = indentBean.getUPhon();
            // 商品ID和数量
            String codAll = indentBean.getCodAll();
            // 下单时间
            LocalDateTime inStartTime = indentBean.getInStartTime();
            // 订单总花费
            BigDecimal inSum = indentBean.getInSum();
            // 优惠卷ID
            Integer cpId = indentBean.getCpId();
            // 判断是否能取消
            //获取当前时间的年月日
            LocalDateTime nowDateTime = LocalDateTime.now();
            if (isOverTime(nowDateTime, inStartTime, !isLunch(nowDateTime, inStartTime))) {
                //超出时间
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "取消订单失败，午餐只能在12点前取消，晚餐只能在20点前取消");
            }
            // 订单设置为取消
            indentBean.setInFlag(CANCELED);
            boolean updateIndentBean = this.updateById(indentBean);
            // 用户金额恢复
            UserBean userBean = userService.getById(uId);
            Double uBalanc = userBean.getUBalanc();
            inSum.add(BigDecimal.valueOf(uBalanc));
            userBean.setUBalanc(inSum.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue() + uBalanc);
            boolean updateUserBean = userService.updateById(userBean);


            //商品库存恢复
            // 商品ID和商品数量
            List<CommodityInfoBo> commodityInfoBoList = JSONUtil.toList(indentBean.getCodAll(), CommodityInfoBo.class);
            List<CommodityBean> commodityBeans = commodityInfoBoList.stream().map(commodityInfoBo -> {
                // 转商品
                CommodityBean commodityBean = commodityService.getById(commodityInfoBo.getCodId());
                commodityBean.setCodStock(commodityBean.getCodStock() + commodityInfoBo.getCodNum());
                return commodityBean;
            }).collect(Collectors.toList());
            boolean updateCommodityBeans = commodityService.updateBatchById(commodityBeans);
            // 优惠卷消费记录删除
            boolean removeCouponLogsBean = true;
            boolean updateCouponReceiveBean = true;
            boolean updateCouponBean = true;
            if (!cpId.equals(NOT_USE_COUPON)) {
                //优惠卷设置为未使用或者已经过期
                // 查询优惠卷领取信息
                CouponReceiveBean couponReceiveBean = couponReceiveService.getOne(new LambdaQueryWrapper<CouponReceiveBean>()
                        .eq(CouponReceiveBean::getUId, uId)
                        .eq(CouponReceiveBean::getCouponId, cpId));
                CouponBean couponBean = couponService.getById(cpId);
                if (couponBean == null || couponReceiveBean == null) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR);
                }
                // 优惠卷数量+1
                couponBean.setCpnum(couponBean.getCpnum() + 1);
                updateCouponBean = couponService.updateById(couponBean);
                // 获取优惠卷结束时间
                Date endTime = couponBean.getEndTime();
                LocalDate endDate = LocalDate.parse(getDate(endTime));
                LocalDate nowDate = LocalDate.now();
                if (nowDate.isAfter(endDate)) {
                    // 优惠卷到期
                    couponReceiveBean.setStatus(EXPIRE);
                } else {
                    couponReceiveBean.setStatus(NOT_USED);
                }
                updateCouponReceiveBean = couponReceiveService.updateById(couponReceiveBean);
                //使用了优惠卷
                removeCouponLogsBean = couponLogsService.remove(new LambdaQueryWrapper<CouponLogsBean>()
                        .eq(CouponLogsBean::getUId, uId)
                        .eq(CouponLogsBean::getOrderNumber, inId)
                        .eq(CouponLogsBean::getCouponReceiveId, cpId));
            }
            if (!updateIndentBean || !updateUserBean || !updateCommodityBeans || !removeCouponLogsBean || !updateCouponReceiveBean || !updateCouponBean) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR);
            }
            // 订单取消成功
            InviteRecord inviteRecord = inviteRecordService.getOne(new LambdaQueryWrapper<InviteRecord>().eq(InviteRecord::getInviteeId, userBean.getUId()));
            if (inviteRecord != null && inviteRecord.getFlag().equals(FIRST_ORDER_PROGRESSING)) {
                // 下单用户是被别人邀请的注册的，并且是第一次下单
                inviteRecord.setFlag(FIRST_ORDER).setRemark(INVITATION_SUCCESS_REMARKS);
                boolean update = inviteRecordService.updateById(inviteRecord);
            }
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized void confirmIndent(Long inId) {
        if (inId == null || inId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        IndentBean indentBean = this.getById(inId);
        if (indentBean == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "订单不存在");
        }
        indentBean.setInFlag(COMPLETE);
        boolean updateById = this.updateById(indentBean);
        if (!updateById) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR);
        }
        // 获取用户ID
        Long uId = indentBean.getUId();
        //查询该用户这单是不是首单
        Long count = indentMapper.selectCount(new LambdaQueryWrapper<IndentBean>().eq(IndentBean::getUId, uId));
        if (count == 1) {
            // 首单
            // 下单基本完成，开始奖励
            InviteRecord inviteRecord = inviteRecordService.getOne(new LambdaQueryWrapper<InviteRecord>().eq(InviteRecord::getInviteeId, uId));
            if (inviteRecord != null && inviteRecord.getFlag().equals(FIRST_ORDER_PROGRESSING)) {
                // 下单用户是被别人邀请的注册的，并且是订单正在进行中
                inviteRecord.setFlag(FIRST_ORDER_COMPLETED).setRemark(TASK_COMPLETED_REMARKS);
                boolean update = inviteRecordService.updateById(inviteRecord);
                if (!update) {
                    throw new BusinessException(ErrorCode.OPERATION_ERROR);
                }
                // 查询邀请人信息
                UserBean inviter = userService.getById(inviteRecord.getInviterId());
                // 保存奖励记录并且给与奖励
                inviteRewardService.saveInviteRewardInfoAndPulsBalance(inviter.getUId(), uId);
            }
        }
    }

    private String getDate(Date date) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String format = simpleDateFormat.format(date);
        return format;
    }

    /**
     * 判断是否是午餐
     *
     * @param nowDate   现在的时间
     * @param startTime 订单开始的时间
     * @return
     */
    private boolean isLunch(LocalDateTime nowDate, LocalDateTime startTime) {
        // 一天开始的时间
        LocalDateTime beginOfDay = LocalDateTimeUtil.beginOfDay(nowDate);
        //午餐的时间
        LocalDateTime lunchTime = LocalDateTimeUtil.offset(beginOfDay, LUNCH_TIME, ChronoUnit.HOURS);
        return startTime.isAfter(lunchTime);
    }

    private boolean isOverTime(LocalDateTime nowDate, LocalDateTime startTime, boolean isLunch) {
        // 一天开始的时间
        LocalDateTime beginOfDay = LocalDateTimeUtil.beginOfDay(nowDate);
        //午餐截至时间
        LocalDateTime overTime = LocalDateTimeUtil.offset(beginOfDay,
                isLunch ? OVER_LUNCH_TIME : OVER_DINNER_TIME,
                ChronoUnit.HOURS);
        return nowDate.isAfter(overTime);
    }
}
